/*-----------------------------------------------------------------------*/
/* Stellaris USB module                                                  */
/*-----------------------------------------------------------------------*/
/*-----------------------------------------------------------------------*/

/*
 * This file was modified from a sample available from the FatFs
 * web site. It was modified to work with the SitaraWare USB stack.
 */
#include "rdiskio.h"
#include "IFDInterface.h"
#include "HAL.h"
#include "configure.h"



#include "hw_types.h"
#include "mmcsd_proto.h"
#include "hs_mmcsdlib.h"
#include "hs_mmcsd.h"
#include "beaglebone.h"
#include "edma_event.h"
#include "soc_AM335x.h"
#include "interrupt.h"
#include "edma.h"



/******************************************************************************
**                      INTERNAL MACRO DEFINITIONS
*******************************************************************************/

/* Frequency */
#define HSMMCSD_IN_FREQ                96000000 /* 96MHz */
#define HSMMCSD_INIT_FREQ              400000   /* 400kHz */

#define HSMMCSD_CARD_DETECT_PINNUM     6

/* EDMA3 Event queue number. */
#define EVT_QUEUE_NUM                  0
 
/* EDMA3 Region Number. */
#define REGION_NUMBER                  0

/* Block size config */
#define HSMMCSD_BLK_SIZE               512
#define HSMMCSD_RW_BLK                 1

/* Global data pointers */
#define HSMMCSD_DATA_SIZE              512



/* MMCSD instance related macros. */

#define MMCSD_INT_NUM                  (SYS_INT_MMCSD0INT)


/* EDMA Events */
#define MMCSD_TX_EDMA_CHAN             (EDMA3_CHA_MMCSD0_TX)
#define MMCSD_RX_EDMA_CHAN             (EDMA3_CHA_MMCSD0_RX)


//the three number-- can i change them?

typedef struct _fatDevice
{
    /* Pointer to underlying device/controller */
    void *dev;

	/* state */
	unsigned int initDone;
	unsigned int cardstate;
	unsigned int transferstate;	//DMA transfer
					//transferstate = 0 <-- not in transfer
					// = 1 <--  transfer up

}fatDevice;





/*-----------------------------------------------------------------------*/
/* 	Static Function                                                  */
/*-----------------------------------------------------------------------*/

static unsigned int HSMMCSDXferStatusGet(mmcsdCtrlInfo *ctrl)
{
	unsigned int status = 0;
	fatDevice* obj = (DMAGetOwner(MMCSD_TX_EDMA_CHAN));
//    unsigned int state = obj->cardstate;

#define __obj_state	obj->cardstate

    volatile unsigned int timeOut = 0xFFFF;

	unsigned int state;

    while ((!(__obj_state & HS_MMCSD_STAT_TRNFCOMP)) && (!(__obj_state & HS_MMCSD_STAT_TRNFCOMP)));

    if (__obj_state & HS_MMCSD_STAT_TRNFCOMP)
    {
        status = 1;
        __obj_state &= (~HS_MMCSD_STAT_TRNFCOMP);
    }

    if (__obj_state & HS_MMCSD_STAT_TRNFCOMP)
    {
        status = 0;
        __obj_state &= (~HS_MMCSD_STAT_DATATIMEOUT);
    }

    /* Also, poll for the callback */
    if (HWREG(ctrl->memBase + MMCHS_CMD) & MMCHS_CMD_DP)
    {
        while(obj->transferstate == 0 && ((timeOut--) != 0));
        obj->transferstate = 0;

        if(timeOut == 0)
        {
            status = 0;
        }
    }
#undef __obj_state
    ctrl->dmaEnable = 0;

    return status;
}

static unsigned int HSMMCSDCmdStatusGet(mmcsdCtrlInfo *ctrl)
{
	unsigned int status = 0;
	fatDevice* obj = (DMAGetOwner(MMCSD_TX_EDMA_CHAN));
//    unsigned int state = obj->cardstate;

#define __obj_state	obj->cardstate


	
    while ((!(__obj_state & HS_MMCSD_STAT_CMDCOMP)) && (!(__obj_state & HS_MMCSD_STAT_CMDCOMP)));

    if (__obj_state & HS_MMCSD_STAT_CMDCOMP)
    {
        status = 1;
        obj->cardstate &= (~HS_MMCSD_STAT_CMDCOMP);
    }

    if (__obj_state & HS_MMCSD_STAT_CMDCOMP)
    {
        status = 0;
        obj->cardstate &= (~HS_MMCSD_STAT_CMDTIMEOUT);
    }
#undef __obj_state
    return status;
}


void HSMMCSDRxDmaConfig(void *ptr, unsigned int blkSize, unsigned int nblks)
{
	fatDevice* obj = (DMAGetOwner(MMCSD_TX_EDMA_CHAN));
	mmcsdCtrlInfo *ctrlInfo = obj->dev;
    _DMAPar paramSet;

    paramSet.srcAddr    = ctrlInfo->memBase + MMCHS_DATA;
    paramSet.destAddr   = (unsigned int)ptr;
    paramSet.srcBIdx    = 0;
    paramSet.srcCIdx    = 0;
    paramSet.destBIdx   = 4;
    paramSet.destCIdx   = (unsigned short)blkSize;
    paramSet.aCnt       = 0x4;
    paramSet.bCnt       = (unsigned short)blkSize/4;
    paramSet.cCnt       = (unsigned short)nblks;
    paramSet.bCntReload = 0x0;
    paramSet.linkAddr   = 0xffff;
    paramSet.opt        = 0;

    /* Set OPT */
    paramSet.opt |= ((MMCSD_RX_EDMA_CHAN << EDMA3CC_OPT_TCC_SHIFT) & EDMA3CC_OPT_TCC);

    /* 1. Transmission complition interrupt enable */
    paramSet.opt |= (1 << EDMA3CC_OPT_TCINTEN_SHIFT);

    /* 2. Read FIFO : SRC Constant addr mode */
    paramSet.opt |= (1 << 0);

    /* 3. SRC FIFO width is 32 bit */
    paramSet.opt |= (2 << 8);

    /* 4.  AB-Sync mode */
    paramSet.opt |= (1 << 2);


    /* Enable the transfer */
    DMATransferSetup(MMCSD_RX_EDMA_CHAN,&paramSet, DMA_MODE_PASSIVE);

    /* configure PaRAM Set */
//    EDMA3SetPaRAM(EDMA_INST_BASE, MMCSD_RX_EDMA_CHAN, &paramSet);

    /* Enable the transfer */
//   EDMA3EnableTransfer(EDMA_INST_BASE, MMCSD_RX_EDMA_CHAN, EDMA3_TRIG_MODE_EVENT);
}

void HSMMCSDTxDmaConfig(void *ptr, unsigned int blkSize, unsigned int blks)
{
	fatDevice* obj = (DMAGetOwner(MMCSD_TX_EDMA_CHAN));
	mmcsdCtrlInfo *ctrlInfo = obj->dev;
    _DMAPar paramSet;

    paramSet.srcAddr    = (unsigned int)ptr;
    paramSet.destAddr   = ctrlInfo->memBase + MMCHS_DATA;
    paramSet.srcBIdx    = 4;
    paramSet.srcCIdx    = blkSize;
    paramSet.destBIdx   = 0;
    paramSet.destCIdx   = 0;
    paramSet.aCnt       = 0x4;
    paramSet.bCnt       = (unsigned short)blkSize/4;
    paramSet.cCnt       = (unsigned short)blks;
    paramSet.bCntReload = 0x0;
    paramSet.linkAddr   = 0xffff;
    paramSet.opt        = 0;

    /* Set OPT */
    paramSet.opt |= ((MMCSD_TX_EDMA_CHAN << EDMA3CC_OPT_TCC_SHIFT) & EDMA3CC_OPT_TCC);

    /* 1. Transmission complition interrupt enable */
    paramSet.opt |= (1 << EDMA3CC_OPT_TCINTEN_SHIFT);

    /* 2. Read FIFO : DST Constant addr mode */
    paramSet.opt |= (1 << 1);

    /* 3. DST FIFO width is 32 bit */
    paramSet.opt |= (2 << 8);

    /* 4.  AB-Sync mode */
    paramSet.opt |= (1 << 2);


    /* Enable the transfer */
    DMATransferSetup(MMCSD_TX_EDMA_CHAN,&paramSet, DMA_MODE_PASSIVE);
}



static void HSMMCSDXferSetup(mmcsdCtrlInfo *ctrl, unsigned char rwFlag, void *ptr,
                             unsigned int blkSize, unsigned int nBlks)
{
	fatDevice* fd = DMAGetOwner(MMCSD_TX_EDMA_CHAN);
	fd->transferstate = 0;
    if (rwFlag == 1)
    {
        HSMMCSDRxDmaConfig(ptr, blkSize, nBlks);
    }
    else
    {
        HSMMCSDTxDmaConfig(ptr, blkSize, nBlks);
    }

    ctrl->dmaEnable = 1;
    HSMMCSDBlkLenSet(ctrl->memBase, blkSize);
}

static void callback(unsigned int tccNum, unsigned int status)
{
    //Get owner
	fatDevice* obj = DMAGetOwner(tccNum);
	obj->transferstate = 1;
    DMATransferRemove(tccNum);
}

static void HSMMCSDIsr(void)
{
    volatile unsigned int status = 0;
	fatDevice* obj = (DMAGetOwner(MMCSD_TX_EDMA_CHAN));
	mmcsdCtrlInfo *ctrlInfo = obj->dev;

    status = HSMMCSDIntrStatusGet(ctrlInfo->memBase, 0xFFFFFFFF);
	fatDevice* fd = DMAGetOwner(MMCSD_TX_EDMA_CHAN);
	fd->cardstate = status;
    
    HSMMCSDIntrStatusClear(ctrlInfo->memBase, status);

}

/*-----------------------------------------------------------------------*/
/* Initialize Disk Drive                                                 */
/*-----------------------------------------------------------------------*/

static void HSMMCSDControllerSetup(mmcsdCtrlInfo* ctrlInfo,unsigned int memBase)
{
	
    ctrlInfo->memBase = memBase;
    ctrlInfo->ctrlInit = HSMMCSDControllerInit;
    ctrlInfo->xferSetup = HSMMCSDXferSetup;
    ctrlInfo->cmdStatusGet = HSMMCSDCmdStatusGet;
    ctrlInfo->xferStatusGet = HSMMCSDXferStatusGet;
    /* Use the funciton HSMMCSDCDPinStatusGet() to use the card presence
       using the controller.
    */
    ctrlInfo->cardPresent = HSMMCSDCardPresent;
    ctrlInfo->cmdSend = HSMMCSDCmdSend;
    ctrlInfo->busWidthConfig = HSMMCSDBusWidthConfig;
    ctrlInfo->busFreqConfig = HSMMCSDBusFreqConfig;
    ctrlInfo->intrMask = (HS_MMCSD_INTR_CMDCOMP | HS_MMCSD_INTR_CMDTIMEOUT |
                            HS_MMCSD_INTR_DATATIMEOUT | HS_MMCSD_INTR_TRNFCOMP);
    ctrlInfo->intrEnable = HSMMCSDIntEnable;
    ctrlInfo->busWidth = (SD_BUS_WIDTH_1BIT | SD_BUS_WIDTH_4BIT);
    ctrlInfo->highspeed = 1;
    ctrlInfo->ocr = (SD_OCR_VDD_3P0_3P1 | SD_OCR_VDD_3P1_3P2);
    ctrlInfo->card = __Malloc(sizeof(mmcsdCardInfo));
    ctrlInfo->ipClk = HSMMCSD_IN_FREQ;
    ctrlInfo->opClk = HSMMCSD_INIT_FREQ;
    ctrlInfo->cdPinNum = HSMMCSD_CARD_DETECT_PINNUM;
	
	(ctrlInfo->card)->ctrl = ctrlInfo;
	
}


FRESULT
rdisk_initialize(
    void** pobj)                /* Physical drive number (0) */
{
	unsigned int status;
   	unsigned int memBase = *pobj;
	*pobj = __Malloc(sizeof(fatDevice));
	fatDevice* obj = *pobj;
	obj->initDone = 0;
	obj->cardstate = 0;
	obj->dev = __Malloc(sizeof(mmcsdCtrlInfo));
		HSMMCSDControllerSetup(obj->dev,memBase);
	//Init the dma to the mmcsd
	DMARequest(MMCSD_TX_EDMA_CHAN,callback,obj);
	DMARequest(MMCSD_RX_EDMA_CHAN,callback,obj);
	
	AddInterrupt(MMCSD_INT_NUM,0,HSMMCSDIsr,NULL);	

	HSMMCSDPinMuxSetup();
	HSMMCSDModuleClkConfig();
	MMCSDCtrlInit(obj->dev);
    	MMCSDIntEnable(obj->dev);

    if ((((fatDevice*)obj)->initDone != 1))
    {
        mmcsdCardInfo *card = (mmcsdCardInfo *) (((mmcsdCtrlInfo*)(((fatDevice*)obj)->dev))->card);
        
        /* SD Card init */
        status = MMCSDCardInit(card->ctrl);

        if (status == 0)
        {
            UARTPuts("\r\nCard Init Failed \r\n", -1);
            
            return FR_NOT_READY;
        }
        else
        {
#if DEBUG				
            if (card->cardType == MMCSD_CARD_SD)
            {
                UARTPuts("\r\nSD Card ", -1);
                UARTPuts("version : ",-1);
                UARTPutNum(card->sd_ver);
    
                if (card->highCap)
                {
                    UARTPuts(", High Capacity", -1);
                }
    
                if (card->tranSpeed == SD_TRANSPEED_50MBPS)
                {
                    UARTPuts(", High Speed", -1);
                }
            }
            else if (card->cardType == MMCSD_CARD_MMC)
            {
                UARTPuts("\r\nMMC Card ", -1);
            }
#endif            
            /* Set bus width */
            if (card->cardType == MMCSD_CARD_SD)
            {
                MMCSDBusWidthSet(card->ctrl);
            }
    
            /* Transfer speed */
            MMCSDTranSpeedSet(card->ctrl);
        }

		((fatDevice*)obj)->initDone = 1;
    }
        
    return 0;
}



/*-----------------------------------------------------------------------*/
/* Returns the current status of a drive                                 */
/*-----------------------------------------------------------------------*/

FRESULT rdisk_status (const char* path,_info* info)                   /* Physical drive number (0) */
{
	info->attribute = AM_SYS | AM_DRV;
	_Memcpy("0",info->name,2);
	info->date	= 0;
	info->time	= 0;
	return FR_OK;
}



/*-----------------------------------------------------------------------*/
/* This function reads sector(s) from the disk drive                     */
/*-----------------------------------------------------------------------*/

FRESULT rdisk_read (
    void** obj,               /* Physical drive number (0) */
    char* buff,             /* Pointer to the data buffer to store read data */
    DWORD count,			/* Sector count (1..255) */
    DWORD sector           /* Physical drive nmuber (0) */
    )             
{

//		mmcsdCardInfo *card = ((mmcsdCtrlInfo*)((*((fatDevice**)obj))->dev))->card;

    	/* READ BLOCK */
		if (MMCSDReadCmdSend((*((fatDevice**)obj))->dev, buff, sector, (unsigned char)count) == 1)
		{
        	return FR_OK;
		}
    

    return FR_RW_ERROR;
}



/*-----------------------------------------------------------------------*/
/* This function writes sector(s) to the disk drive                     */
/*-----------------------------------------------------------------------*/

#if _READONLY == 0
FRESULT rdisk_write (
    void** obj,           /* Physical drive number (0) */
    const char* buff,       /* Pointer to the data to be written */
    DWORD count,
    DWORD sector           /* Start sector number (LBA) */
    )             /* Sector count (1..255) */
{

//		mmcsdCardInfo *card = ((mmcsdCtrlInfo*)((*((fatDevice**)obj))->dev))->card;
    	/* WRITE BLOCK */
	    if(MMCSDWriteCmdSend((*((fatDevice**)obj))->dev,(BYTE*) buff, sector, count) == 1)
		{
        	return FR_OK;
		}
	

    return FR_RW_ERROR;
}
#endif /* _READONLY */

/*-----------------------------------------------------------------------*/
/* Miscellaneous Functions                                               */
/*-----------------------------------------------------------------------*/

FRESULT rdisk_ioctl (
    void* obj,               /* Physical drive number (0) */
    BYTE ctrl,              /* Control code */
    void *buff)             /* Buffer to send/receive control data */
{
	return FR_OK;
}

/*---------------------------------------------------------*/
/* User Provided Timer Function for FatFs module           */
/*---------------------------------------------------------*/
/* This is a real time clock service to be called from     */
/* FatFs module. Any valid time must be returned even if   */
/* the system does not support a real time clock.          */

DWORD rget_fattime (void)
{
    return    ((2007UL-1980) << 25) // Year = 2007
            | (6UL << 21)           // Month = June
            | (5UL << 16)           // Day = 5
            | (11U << 11)           // Hour = 11
            | (38U << 5)            // Min = 38
            | (0U >> 1)             // Sec = 0
            ;
}

#ifdef ROOT_DISK_IS_MMCSD

_file_op root_diskop = {
.file_mount   = rdisk_initialize,
.file_state  = rdisk_status,
.file_read   = rdisk_read,
.file_write  = rdisk_write,
.file_ioctl  = rdisk_ioctl,
};

#endif
